//
// Created by DELL on 2024/7/22.
//

#include <iostream>
#include <random>
#include <coroutine>

// 视频教程地址
// https://www.bilibili.com/video/BV1c8411f7dw

// Suspend(false) -> std::suspend_never
// Suspend(true) -> std::suspend_always
struct Suspend
{
    explicit Suspend(bool suspend): suspend(suspend) {}
    Suspend(bool suspend, const char* name): suspend(suspend), name(name) {}

    bool suspend{true};
    const char* name{""};

    [[nodiscard]] bool await_ready() const noexcept {
        std::cout << "SuspendNever::await_ready, ready: " << (!suspend) << " , " << name << std::endl;
        return !suspend;
    }

    void await_suspend(std::coroutine_handle<>) const noexcept
    {
        std::cout << "SuspendNever::await_suspend, " << name << std::endl;
    }

    void await_resume() const noexcept
    {
        std::cout << "SuspendNever::await_resume, " << name << std::endl;
    }
};

struct CoRet
{
    struct promise_type
    {
        int _in{-1};
        int yieldReturn{-1};
        int res{-1};

        // 进入协程函数时会调用，这个值会存放在堆中，直到整个协程调用destroy才会释放
        CoRet get_return_object()
        {
            std::cout << "CoRet::promise_type::get_return_object" << std::endl;
            return {std::coroutine_handle<promise_type>::from_promise(*this)};
        }

        // 协程开始时调用
        Suspend initial_suspend() noexcept
        {
            std::cout << "CoRet::promise_type::initial_suspend" << std::endl;
            return {false, "initial_suspend"};
        }

        // 协程函数结束时调用
        // Suspend(true) 表示协程结束后保留结果，后续不能调用resume，
        // Suspend(false)表示会销毁整个协程
        Suspend final_suspend() noexcept
        {
            std::cout << "CoRet::promise_type::final_suspend" << std::endl;
            return {true, "final_suspend"};;
        }

        Suspend yield_value(int r) noexcept
        {
            std::cout << "CoRet::promise_type::yield_value" << std::endl;
            yieldReturn = r;
            return {true, "yield_value"};;
        }

        // return_value 和 return_void 只能有一个
        void return_value(int r)
        {
            std::cout << "CoRet::promise_type::return_value" << std::endl;
            res = r;
        }

//        void return_void()
//        {
//            std::cout << "CoRet::promise_type::return_void" << std::endl;
//        }

        void unhandled_exception()
        {
            std::cout << "CoRet::promise_type::unhandled_exception" << std::endl;
        }
    };

    std::coroutine_handle<promise_type> handle;
};

struct Input
{
    CoRet::promise_type* promise{};

    bool await_ready()
    {
        std::cout << "Input::await_ready, ready: false" << std::endl;
        // false 代表 co_await 要暂停，返回到 await_suspend 指定的执行位置
        // true 代表 co_await 不需要暂停 继续执行
        return false;
    }

    void await_suspend(const std::coroutine_handle<CoRet::promise_type>& handle)
    {
        std::cout << "Input::await_suspend" << std::endl;

        // 返回void表示返回到调用者，这里代表 main 函数
        // 返回其他的 coroutine_handle 则会执行对应的携程

        promise = &handle.promise();
    }

    int await_resume()
    {
        std::cout << "Input::await_resume" << std::endl;

        // 返回对应 co_await 的返回
        return promise->_in;
    }

};

CoRet Guess()
{
    // 携程函数启动时，会有以下操作
//    CoRet::promise_type promise;
//    CoRet ret = promise.get_return_object();
//    co_await promise.initial_suspend();

    std::cout << "Guess: Start" << std::endl;
    std::random_device rd;
    std::default_random_engine randomEngine{rd()};
    std::uniform_int_distribution<> distr(0,30);
    int res = distr(randomEngine);

    Input input;
    int g = co_await input;

    std::cout << "Coroutine : You guess "<< g << std::endl;

    co_yield (res == g ? 0 : ((g > res) ? 1 : -1));


    std::cout << "Guess: Finish" << std::endl;

    // 如果不要返回值，可以定义 return_void， 结束前会自动调用return_void函数，无需调用 co_return
    co_return res;
//    ret._h.promise().return_void();

    // 协程函数结束时，会有以下操作
//    co_await promise.final_suspend();
}


int main()
{
    std::cout << "Main: Start" << std::endl;
    auto ret = Guess();
    std::cout << "Main: make a guess..." << std::endl;

    ret.handle.promise()._in = 15;
    ret.handle.resume(); // resume form co_await

    auto yieldValue = ret.handle.promise().yieldReturn;
    std::cout << "Main: yield return:" << yieldValue << std::endl;

    ret.handle.resume(); // resume form co_yield

    // 调用了 co_return 后 ret._h.done() 会返回 true
    if (ret.handle.done())
    {
        std::cout << "Main: done, res:" << ret.handle.promise().res << std::endl;
    }

    // 手动销毁数据，销毁后不能在获取携程的运行结果
//    ret._h.destroy();

    std::cout << "Main: Finish" << std::endl;

    return 0;
}